using System;
using System.Collections.Generic;
using WDFactoryLib;

namespace CardsEngine.Poker
{
	public class PokerHandEvaluator: FactoryUser
	{
        private IDictionary<Hand, UInt64> handValueLookupTable;

        public IMathFunctionProvider math;
        
        public PokerHandEvaluator()
            :base()
        {
            math = new PokerTestKit.BasicMathProvider();
            InitializeInstance();
        }

        public PokerHandEvaluator(IMathFunctionProvider _math)
            :base()
        {
            math = _math;
            InitializeInstance();
        }

        internal PokerHandEvaluator(IFactory _factory)
            :base(_factory)
        {
            math = new PokerTestKit.BasicMathProvider();
            InitializeInstance();
        }

        internal PokerHandEvaluator(IFactory _factory, IMathFunctionProvider _math)
            :base(_factory)
        {
            math = _math;
            InitializeInstance();
        }

        private void InitializeInstance()
        {
            handValueLookupTable = Factory.GetNewDictionary<Hand, UInt64>();
        }

        protected ISet<IList<Card>> GetAllSubsets(IList<Card> cards, int subsetSize)
        {

            IList<Card> current;
            ISet<IList<Card>> result = Factory.GetNewSet<IList<Card>>();
            int totalNumCards = cards.Count;

            if (totalNumCards < subsetSize)
                throw new ArgumentException("The size (Count) of the cards list must be at least 5");

            // Initial value to subsetSize many 1's in the LO sector
            // i.e. if subsetSize=5 then choose's initial value should be 0000 ... 0001 1111
            // Such a value indicates that the first loop iteration will choose the cards at indices 0-4
            uint choose = 0;
            for (int x = 0; x < subsetSize; ++x)
                choose |= (uint)(1 << x);

            // The maximum value allowed for "choose"
            // ex if totalNumCards is 7
            // then max_choose_value = 0000 ... 0111 1100 = 0x7C
            uint max_choose_value = choose << (totalNumCards-subsetSize);

            while (choose < max_choose_value)
            {
                current = Factory.GetNewList<Card>();
                for (int i = 0; i < totalNumCards; i++)
                {
                    // If the i'th bit of 'choose' is set
                    if ((choose & (1 << i)) != 0)
                        current.Add(cards[i]);
                }

                result.Add(current);

                // This is a lazy way of doing the increment...
                // There's probably a better way to do it using bitwise operations
                // With the current method, we simply increment by 1 repeatedly until
                // the hamming weight is equal to the subset size
                do { ++choose; } while (math.ComputeHammingWeight32(choose) != subsetSize);
            }

            return result;
        }

        public virtual UInt64 ComputeFiveCardHandStrength(IList<Card> cards)
        {
            if (cards.Count < 5)
                throw new ArgumentException("There must be at least 5 cards to compute the strength of the hand using this method");
            else if (cards.Count > 5)
            {
                // TODO - Compute hand strength of all possible 5-card sets,
                // then select the set with the highest strength.
                ISet<IList<Card>> fiveCardSets = GetAllSubsets(cards, 5);

            }

            throw new NotImplementedException();
        }
	}
}

